"""
MeadowbankSource is the feeder for hydro sensors located at Meadowbank.
This feeder iterates through each valid file specified by the
path provided by the delegate configuration, parses those files and 
dispatches all valid samples. 

@author Ben Howell <ben.howell@csiro.au>
"""

import time
import sys
import os
import re
import string
import threading
from threading import Thread, Event, InterruptedException
from sleeper import Sleeper

from java.lang import Boolean

from com.xhaus.jyson import JysonCodec as json

from org.joda.time import DateTimeZone
from org.joda.time.format import DateTimeFormat 

from au.csiro.ict.tasman.core import Message
from au.csiro.ict.tasman.util import QuickCache
from au.csiro.ict.tasman.datatype import Sample


def run():
  """
  Called by au.csiro.ict.tasman.source.ScriptSource.
  This is the entry point for this script.
  @return the running script instance.
  """
  return MeadowbankSource()
  

def isRunning():
  """
  Called by au.csiro.ict.tasman.source.ScriptSource.
  If false, SourceManager will clean up calling thread and attempt a restart on
  the Java side.
  @return true if running, else false.
  """
  if instance is not None:
    if instance.is_running():
      return Boolean("True")
  return Boolean("False")


def shutDown():
  """
  Called by au.csiro.ict.tasman.source.ScriptSource.
  This allows script to perform its own cleanup routine before exiting.
  @return true after shutdown.
  """
  instance.shut_down()


class MeadowbankSource():
  """
  Main data source routine.
  """
  def __init__(self):
    """
    Constructor.
    """
    import re
    self.sleeper = Sleeper()
    self.thread_cancelled = False
    self.configuration = json.loads(source.getConfiguration().getConfiguration().toString())
    self.file_name_pattern = re.compile(self.configuration['filenamepattern'])
    self.row_pattern = re.compile(self.configuration['rowpattern'])
    self.interval = self.configuration['interval']
    self.file_dir = os.path.abspath(source.getDataDirectory() + self.configuration['path'])
    self.cache = QuickCache(self.file_dir + "/cache.json")
    self.datetime_format = DateTimeFormat.forPattern(self.configuration['timestamp'])
    self.datetime_format = self.datetime_format.withZone(DateTimeZone.UTC)
    self.thread = Thread(target=self.run)
    self.thread.start()
    source.getLogger().info("MeadowbankSource initialised")
    

  def run(self):
    """
    Main loop.
    """
    while not self.thread_cancelled:
      try:
	for file_name in os.listdir(self.file_dir):
	  match = self.file_name_pattern.match(file_name)
	  if match and not self.cache.contains(file_name):
	    self.cache.add(file_name)
	    sensor_id = "hydro.meadowbank.flow"
	    messages = self.parse_file(file_name, sensor_id)
	    source.dispatch(messages)
	self.cache.save()
	source.getLogger().debug("sleeping...")
	self.sleeper.sleep(self.interval)
	source.getLogger().debug("waking...")
      except InterruptedException:
	self.thread_cancelled = True


  def is_running(self):
    """
    Returns true if main loop is running.
    @return true if main loop is running.
    """
    return self.thread.isAlive()
    
  
  def shut_down(self):
    """
    Performs cleanup routine on shutdown.
    @return true after shutdown.
    """
    source.getLogger().debug("shutting down script...")
    self.thread_cancelled = True
    source.getLogger().debug("forcibly waking thread...")
    self.sleeper.waken()
    #block while waiting for thread to terminate
    while self.thread.isAlive():
      time.sleep(1)
    source.getLogger().debug("script terminated!")
    self.cache.save()
    return True
    

  def parse_file(self, file_name, sensor_id):
    """
    Parses each line in the file_name given, creates samples and
    returns a message collection containing those samples.
    @param file_name name of the file to parse.
    @param sensor_id the sensor id of the observations in the file.
    @return list of messages.
    """
    messages = list()
    file = os.path.join(self.file_dir, file_name)
    try:
      f = open(file, 'r')
    except IOError:
      source.getLogger().error("I/O exception whilst parsing file: " + file)
      self.cache.remove(file)
    else:
      # Parse file.
      source.getLogger().debug("parsing file: " + file_name)
      for line in f:
	match = self.row_pattern.match(line)
	if match:
	  if(match.group(2) != "" and match.group(3) != "missing data"):
	    # Create Sample...
	    s = Sample()
	    s.time = self.datetime_format.parseDateTime(match.group(1))
	    s.value = float(match.group(2))
	    s.sensorId = sensor_id
	    annotation = string.replace(match.group(4), '\r', '')
	    if annotation != "":
	      s.tags.put("annotation", annotation)
	    s.tags.put("uom", "m3/sec")
	    message = Message(s.sensorId, s)
	    messages.append(message)
	  else:
	    source.getLogger().debug("UNUSABLE DATA: " + match.group(0))
      f.close()
    return messages
    